#pragma once
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<pthread.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include"Log.hpp"
#define PORT 8888
#define BACKLOG 5
class TcpServer
{
private:
    int port;
    int listen_sock;
    static TcpServer *svr;//用来获取单例模式的变量

//使用单例模式，构造和拷贝构造都设置为私有化
private:
    TcpServer(int _port ):port(_port),listen_sock(-1)
    {}
    TcpServer(const TcpServer &s) 
    {}
public:
    static TcpServer*getinstance(int port)
    {
        static pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;//锁初始化
        if(nullptr==svr)
        {
            pthread_mutex_lock(&lock);//上锁
            if(nullptr==svr)
            {
                svr=new TcpServer(port);
                svr->InitServer();
            }
            pthread_mutex_unlock(&lock);//解锁
        }
        return svr;
    }

    //初始化Tcp服务器
    void InitServer()
    {
        Socket();
        Bind();
        Listen(); 
        LOG(INFO,"tcp_server init ... sucess");//打印初始化成功日志
    }

    //创建套接字
    void Socket()
    {
        //选择ipv4、tcp面向字节流的套接字
        listen_sock=socket(AF_INET,SOCK_STREAM,0);
        if(listen_sock < 0)
        {
            LOG(FATAL,"socket error!");
            exit(1);
        }
        //处理服务端意外挂掉的情况，服务端意外挂掉后,tcp链接处于timewait状态，使用的端口没有释放
        //使用地址复用的方式让重新启动的服务端能继续使用上一次挂掉没有被释放的端口
        int opt=1;
        setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
        LOG(INFO,"create socket...success!");
    }
    //绑定套接字，将用户写的数据更新进内核
    void Bind()
    {
        struct sockaddr_in local;//定义存储客户端（浏览器内置了客户端）的结构体
        memset(&local,0,sizeof(local));
        local.sin_family=AF_INET;
        local.sin_port=htons(port);
        local.sin_addr.s_addr=INADDR_ANY;//任何一个ip都能连接我的服务器
        //bind系统接口把用户数据更新进内核
        if(bind(listen_sock, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            LOG(FATAL,"bind error!");
            exit(2);
        }
        LOG(INFO,"bind socket ... success!");
    }
    //监听（浏览器）客户端发来的连接请求
    void Listen()
    {
        if(listen(listen_sock, BACKLOG) < 0 )
        {
            LOG(FATAL,"listen socket error!");
            exit(3);
        }
        LOG(INFO,"listen socket ... success!");
    }
    int Sock()
    {
        return listen_sock;
    }
    ~TcpServer()
    {
        if(listen_sock >= 0) close(listen_sock);//关闭监听套接字
    }

};

TcpServer * TcpServer::svr=nullptr;